import functools
import inspect

from werkzeug.local import Local, LocalProxy

from flow_alchemy.core.settings import log_manage
from flow_alchemy.util.__init__ import local_time
from flow_alchemy.db import session as _session
from flow_alchemy.db.models import Process, RoleMap, NodeConfig
from flow_alchemy.db.work_status import ProcessStatus

local = Local()

local.session = _session
local.log = log_manage

session = LocalProxy(lambda: local.session, 'session')
ctrl = LocalProxy(lambda: local.ctrl, 'ctrl')
task = LocalProxy(lambda: local.task, 'task')
process_manager = LocalProxy(lambda: local.process_manager, 'process_manager')
external = LocalProxy(lambda: local.external, 'external')
log = LocalProxy(lambda: local.log, 'log')


def extraction_parameters(fun, args=None, kwargs=None):
    fun_info=inspect.getfullargspec(fun)
    var_names = fun_info.args
    def_value = fun_info.defaults
    var_values = [None for _ in var_names]
    if args:
        var_values[0:len(args) - 1] = args
    if def_value:
        var_values[len(var_values) - len(def_value) - 1:] = def_value

    parameters = dict(zip(var_names, var_values))
    if kwargs:
        parameters.update(kwargs)
    return parameters


def _function_logging():
    def decorator(fun):
        @functools.wraps(fun)
        def inner(*args, **kwargs):

            parameters = extraction_parameters(fun, args, kwargs)

            if 'self' in parameters:
                parent = parameters.pop('self')
                name = '%s.%s' % (parent.__class__.__name__, fun.__name__)
            else:
                name = '%s' % fun.__name__
            with log.make_function(name=name) as o:
                o.add(**parameters)

                result = fun(*args, **kwargs)
                log.top.add(result=result)
                return result

        return inner

    return decorator


method_logging = _function_logging()
function_logging = _function_logging()


# @function_logging
def update_column(model, **kwargs):
    """修改后立即提交"""
    global log
    model.populate(kwargs)
    commit_models(model)

    return model


@function_logging
def create_process(**kwargs):
    kwargs['tid'] = task.tid
    kwargs['pid'] = max([0] + [_.pid for _ in process_manager.data]) + 1
    if 'status' not in kwargs:
        kwargs['status'] = ProcessStatus.init
    if 'active' not in kwargs:
        kwargs['active'] = True
    kwargs['update_date'] = local_time()
    kwargs['create_date'] = local_time()
    proc = Process.value_of(**kwargs)
    process_manager.data.append(proc)
    return proc


def commit_models(*args):
    with log.make_item(name='save_models') as o:

        for i, e in enumerate(args):
            o.add_pair(e.short_name(), e)
        session.add_all(args)
        session.commit()

        for i, e in enumerate(args):
            o.add_pair('commit %s ' % i, str(e))


@function_logging
def roles_child(*args):
    return session.query(RoleMap).with_entities(RoleMap.rid, RoleMap.uid).filter(RoleMap.rid.in_(args),
                                                                                 RoleMap.enabled == True).all()


@function_logging
def user_parent(*args):
    return session.query(RoleMap).with_entities(RoleMap.rid, RoleMap.uid).filter(RoleMap.uid.in_(args),
                                                                                 RoleMap.enabled == True).all()


@function_logging
def node_config(nid):
    nf = session.query(NodeConfig).filter_by(nid=nid, enabled=True).all()
    data = [(_.rid, _.uid) for _ in nf]
    rids = [_.rid for _ in nf]
    data += [(_.rid, _.uid) for _ in roles_child(rids)]
    dt = {}
    for rid, uid in data:
        dt[uid] = rid
    return [(rid, uid) for uid, rid in dt.items() if uid]
